﻿#include "FITKGraphObjectGeoModelBase.h"

// VTK
#include <vtkObject.h>
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPolyData.h>
#include <vtkPoints.h>

// Graph 
#include "FITKGraphCommons.h"
#include "FITKPartDataSetManager.h"
#include "FITKGraphObjectModelHighlighting.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

// Data
#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoCommand.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoShapeAgent.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKVirtualTopoManager.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeometryMeshVS.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeometryMeshEntity.h"

namespace Exchange
{
    FITKGraphObjectGeoModelBase::FITKGraphObjectGeoModelBase(AbaqusData::FITKDataCase* caseData, Core::FITKAbstractDataObject* dataObj)
        : FITKGraphObjectModelBase(caseData, dataObj)
    {
        // Set the layer need to be rendered.
        setRenderLayer(0, 0);

        // Initialize polygon offset.
        m_polygonOffset = FITKGraphCommons::s_geomPolyOffset;
    }

    void FITKGraphObjectGeoModelBase::init()
    {
        this->Superclass::init();
    }
   
    void FITKGraphObjectGeoModelBase::update(bool forceUpdate)
    {
        // Update visibility.
        updateVisibility();

        // Re-generate the shape.
        bool flag = generateDataSet(forceUpdate);

        // Update the actors.
        if (flag)
        {
            updateActors();
        }
    }

    void FITKGraphObjectGeoModelBase::setPointVisible(bool visible)
    {
        if (m_fActorVertex)
        {
            m_fActorVertex->SetVisibility(visible);
        }
    }

    void FITKGraphObjectGeoModelBase::setTransparent(bool isOn)
    {
        // Get the opacity.
        double opa = isOn ? 1. - FITKGraphCommons::s_transparency : 1.;

        if (m_fActorFace)
        {
            m_fActorFace->GetProperty()->SetOpacity(opa);
        }

        if (m_fActorEdge)
        {
            m_fActorEdge->GetProperty()->SetOpacity(opa);
        }

        if (m_fActorVertex)
        {
            m_fActorVertex->GetProperty()->SetOpacity(opa);
        }
    }

    void FITKGraphObjectGeoModelBase::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!m_graphObjHighlight || !getDataVisibility())
        {
            return;
        }

        m_graphObjHighlight->highlight(color);

        this->Superclass::highlight(color);
    }

    void FITKGraphObjectGeoModelBase::disHighlight()
    {
        if (m_graphObjHighlight && m_isHighlighting)
        {
            m_graphObjHighlight->disHighlight();
        }

        if (m_graphObjPreHighlight && m_isPreHighlighting)
        {
            m_graphObjPreHighlight->disHighlight();
        }

        this->Superclass::disHighlight();
    }

    void FITKGraphObjectGeoModelBase::preHighlight()
    {
        // If the object is invisible, then return.
        if (!m_graphObjPreHighlight || !getDataVisibility())
        {
            return;
        }

        m_graphObjPreHighlight->highlight(FITKGraphCommons::s_preHighlightColor);

        this->Superclass::preHighlight();
    }

    void FITKGraphObjectGeoModelBase::setPickable(bool isPickable, int dim)
    {
        switch (dim)
        {   
        case -1:
        {
            // All.
            for (vtkProp* actor : m_actorList)
            {
                if (actor)
                {
                    actor->SetPickable(isPickable);
                }
            }

            break;
        }
        case 0:
        {
            // Vertex.
            if (m_fActorVertex)
            {
                m_fActorVertex->SetPickable(isPickable);
            }

            break;
        }
        case 1:
        {
            // Edge.
            if (m_fActorEdge)
            {
                m_fActorEdge->SetPickable(isPickable);
            }

            break;
        }
        case 2:
        {
            // Face.
            if (m_fActorFace)
            {
                m_fActorFace->SetPickable(isPickable);
            }

            break;
        }
        default:
            break;
        }
    }

    int FITKGraphObjectGeoModelBase::getCmdIdByVTKCellId(int vtkCellId, GeomShapeType shapeType)
    {
        int id = -1;
        AbaqusData::FITKAbaqusPart* part = getPartData();
        if (!part)
        {
            return id;
        }

        PartGeomDataVTK* data{ nullptr };
        bool flag = FITKPartDataSetManager::getInstance()->getPartGeomData(part->getDataObjectID(), data);
        if (!flag)
        {
            return id;
        }
   
        if (vtkCellId < 0)
        {
            return id;
        }

        switch (shapeType)
        {
        case GST_Vertex:
        {
            // Try to get the vertex.
            if (data->VertexCellIdCmdIdMap.count() <= vtkCellId)
            {
                break;
            }

            // Get the vertex command id.
            id = data->VertexCellIdCmdIdMap[vtkCellId];

            break;
        }
        case GST_Edge:
        {
            // Try to get the edge.
            if (data->EdgeCellIdCmdIdMap.count() <= vtkCellId)
            {
                break;
            }

            // Get the edge command id.
            id = data->EdgeCellIdCmdIdMap[vtkCellId];

            break;
        }
        case GST_Face:
        {
            // Try to get the face.
            if (data->FaceCellIdCmdIdMap.count() <= vtkCellId)
            {
                break;
            }

            // Get the face command id.
            id = data->FaceCellIdCmdIdMap[vtkCellId];

            break;
        }
        default:
            return -1;
        }

        return id;
    }

    int FITKGraphObjectGeoModelBase::getShapeIdByVTKCellId(int vtkCellId, GeomShapeType shapeType)
    {
        int id = -1;
        AbaqusData::FITKAbaqusPart* part = getPartData();
        if (!part)
        {
            return id;
        }

        PartGeomDataVTK* data{ nullptr };
        bool flag = FITKPartDataSetManager::getInstance()->getPartGeomData(part->getDataObjectID(), data);
        if (!flag)
        {
            return id;
        }

        if (vtkCellId < 0)
        {
            return id;
        }

        switch (shapeType)
        {        
        case GST_Vertex:
        {
            // Try to get the vertex.
            if (data->CellIdVertexIdMap.count() <= vtkCellId)
            {
                break;
            }

            // Get the vertex id.
            id = data->CellIdVertexIdMap[vtkCellId];

            break;
        }
        case GST_Edge:
        {
            // Try to get the edge.
            if (data->CellIdEdgeIdMap.count() <= vtkCellId)
            {
                break;
            }

            // Get the edge id.
            id = data->CellIdEdgeIdMap[vtkCellId];

            break;
        }
        case GST_Face:
        {
            // Try to get the face.
            if (data->CellIdFaceIdMap.count() <= vtkCellId)
            {
                break;
            }

            // Get the face id.
            id = data->CellIdFaceIdMap[vtkCellId];

            break;
        }
        default:
            return -1;
        }

        return id;
    }

    const QVector<int> FITKGraphObjectGeoModelBase::getVTKCellIdsByShapeId(int shapeId, GeomShapeType shapeType)
    {
        QVector<int> cellIds;

        AbaqusData::FITKAbaqusPart* part = getPartData();
        if (!part)
        {
            return cellIds;
        }

        PartGeomDataVTK* data{ nullptr };
        bool flag = FITKPartDataSetManager::getInstance()->getPartGeomData(part->getDataObjectID(), data);
        if (!flag)
        {
            return cellIds;
        }  

        switch (shapeType)
        {
        case GST_Vertex:
        {
            if (!data->VertexIdCellIdMap.contains(shapeId))
            {
                break;
            }

            // Get the vertex's cell id.( Vertex cell )
            cellIds.push_back(data->VertexIdCellIdMap[shapeId]);
            break;
        }
        case GST_Edge:
        {
            if (!data->EdgeIdCellIdsMap.contains(shapeId))
            {
                break;
            }

            // Get the edge's cell id.
            cellIds << data->EdgeIdCellIdsMap[shapeId];
            break;
        }
        case GST_Face:
        {
            if (!data->FaceIdCellIdsHash.contains(shapeId))
            {
                break;
            }

            // Get the face's cell ids.
            cellIds = data->FaceIdCellIdsHash[shapeId];
            break;
        }
        default:
            break;
        }

        return cellIds;
    }

    vtkDataSet* FITKGraphObjectGeoModelBase::getMeshByGeomType(GeomShapeType shapeType)
    {
        AbaqusData::FITKAbaqusPart* part = getPartData();
        if (!part)
        {
            return nullptr;
        }

        PartGeomDataVTK* data{ nullptr };
        bool flag = FITKPartDataSetManager::getInstance()->getPartGeomData(part->getDataObjectID(), data);
        if (!flag)
        {
            return nullptr;
        }

        switch (shapeType)
        {
        case GST_Vertex:
            return data->DataSetVertex;
        case GST_Edge:
            return data->DataSetEdge;
        case GST_Face:
            return data->DataSetFace;
        default:
            return nullptr;
        }

        return nullptr;
    }

    bool FITKGraphObjectGeoModelBase::generateDataSet(bool forceUpdate)
    {
        AbaqusData::FITKAbaqusPart* part = getPartData();
        if (!part)
        {
            return false;
        }
        
        // Try to re-generate the dataset.
        PartGeomDataVTK* data{ nullptr };
        bool flag = FITKPartDataSetManager::getInstance()->getPartGeomData(part->getDataObjectID(), data, forceUpdate);
        return flag;
    }

    void FITKGraphObjectGeoModelBase::generateGraph()
    {
        // Create vertex actor.
        m_fActorVertex = FITKGraphActor::New();
        m_fActorVertex->setGraphObject(this);
        m_fActorVertex->setActorType(ActorType::VertexActor);
        m_fActorVertex->SetPickable(true);
        m_fActorVertex->SetVisibility(true);
        m_fActorVertex->GetProperty()->SetPointSize(5);
        m_fActorVertex->GetProperty()->SetRepresentation(2);
        m_fActorVertex->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActorVertex);

        // Create edge actor.
        m_fActorEdge = FITKGraphActor::New();
        m_fActorEdge->setGraphObject(this);
        m_fActorEdge->setActorType(ActorType::EdgeActor);
        m_fActorEdge->SetPickable(true);
        m_fActorEdge->SetVisibility(true);
        m_fActorEdge->GetProperty()->SetLineWidth(2);
        m_fActorEdge->GetProperty()->SetRepresentation(1);
        m_fActorEdge->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActorEdge);

        // Create face actor.
        m_fActorFace = FITKGraphActor::New();
        m_fActorFace->setGraphObject(this);
        m_fActorFace->setActorType(ActorType::SurfaceActor);
        m_fActorFace->SetPickable(true);
        m_fActorFace->SetVisibility(true);
        m_fActorFace->GetProperty()->SetRepresentation(2);
        m_fActorFace->GetProperty()->SetEdgeVisibility(false);
        m_fActorFace->GetProperty()->SetLineWidth(0.);
        m_fActorFace->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActorFace);

        // Set edge color.
        //@{
        m_fActorEdge->setColor(Qt::black);
        //@}

        // Initialize the polygon offset.
        m_fActorFace->setRelativeCoincidentTopologyPolygonOffsetParameters(0.);
        m_fActorEdge->setRelativeCoincidentTopologyPolygonOffsetParameters(-0.5);
        m_fActorVertex->setRelativeCoincidentTopologyPolygonOffsetParameters(-1.);
    }
}   // namespace Exchange